﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

// ReSharper disable InconsistentNaming

namespace Jaiden.Security.Cryptography
{
    public static class RSAExtensions
    {
        #region ConvertKey

        private static byte[] ConvertKey(string keyString)
        {
            if (string.IsNullOrWhiteSpace(keyString)) return new byte[0];
            var rows = keyString.Split('\r', '\n');
            var sb = new StringBuilder();
            foreach (var row in rows)
                if (!string.IsNullOrWhiteSpace(row) && !row.StartsWith("--"))
                    sb.AppendLine(row.Trim());
            return Convert.FromBase64String(sb.ToString());
        }

        #endregion

        #region LoadPKCS8PrivateKey

        /// <summary>
        /// </summary>
        /// <param name="rsa"></param>
        /// <param name="key"></param>
        public static void LoadPKCS8PrivateKey(this RSA rsa, string key)
        {
            LoadPKCS8PrivateKey(rsa, ConvertKey(key));
        }

        /// <summary>
        /// </summary>
        /// <param name="rsa"></param>
        /// <param name="key"></param>
        public static void LoadPKCS8PrivateKey(this RSA rsa, byte[] key)
        {
            using (var memoryStream = new MemoryStream(key))
            {
                using (var binaryReader = new BinaryReader(memoryStream))
                {
                    var num = binaryReader.ReadUInt16();
                    if (num == 33072)
                    {
                        binaryReader.ReadByte();
                    }
                    else
                    {
                        if (num != 33328) throw new NotSupportedException();
                        binaryReader.ReadInt16();
                    }

                    IEnumerable<byte> arg540 = binaryReader.ReadBytes(3);
                    var array = new byte[3];
                    array[0] = 2;
                    array[1] = 1;
                    if (!arg540.SequenceEqual(array)) throw new NotSupportedException();
                    if (!binaryReader.ReadBytes(15).SequenceEqual(new byte[]
                    {
                        48,
                        13,
                        6,
                        9,
                        42,
                        134,
                        72,
                        134,
                        247,
                        13,
                        1,
                        1,
                        1,
                        5,
                        0
                    }))
                        throw new NotSupportedException();
                    if (binaryReader.ReadByte() != 4) throw new NotSupportedException();
                    switch (binaryReader.ReadByte())
                    {
                        case 129:
                            binaryReader.ReadByte();
                            break;
                        case 130:
                            binaryReader.ReadUInt16();
                            break;
                    }

                    rsa.LoadPKCS1PrivateKey(
                        binaryReader.ReadBytes((int) (memoryStream.Length - memoryStream.Position)));
                }
            }
        }

        #endregion

        #region LoadPKCS1PrivateKey

        public static void LoadPKCS1PrivateKey(this RSA rsa, string key)
        {
            LoadPKCS1PrivateKey(rsa, ConvertKey(key));
        }

        /// <summary>
        /// </summary>
        /// <param name="rsa"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static void LoadPKCS1PrivateKey(this RSA rsa, byte[] key)
        {
            using (var memoryStream = new MemoryStream(key))
            {
                using (var binaryReader = new BinaryReader(memoryStream))
                {
                    var num = binaryReader.ReadUInt16();
                    if (num == 33072)
                    {
                        binaryReader.ReadByte();
                    }
                    else
                    {
                        if (num != 33328) throw new NotSupportedException();
                        binaryReader.ReadInt16();
                    }

                    IEnumerable<byte> arg580 = binaryReader.ReadBytes(3);
                    var array = new byte[3];
                    array[0] = 2;
                    array[1] = 1;
                    if (!arg580.SequenceEqual(array)) throw new NotSupportedException();
                    var queue = new Queue<byte[]>();
                    for (var i = 0; i < 8; i++)
                    {
                        if (binaryReader.ReadByte() != 2) throw new NotSupportedException();
                        int num2 = binaryReader.ReadByte();
                        if (num2 == 129)
                            num2 = binaryReader.ReadByte();
                        else if (num2 == 130) num2 = (binaryReader.ReadByte() << 8) | binaryReader.ReadByte();
                        while (binaryReader.ReadByte() == 0) num2--;
                        binaryReader.BaseStream.Seek(-1L, SeekOrigin.Current);
                        queue.Enqueue(binaryReader.ReadBytes(num2));
                    }

                    rsa.ImportParameters(new RSAParameters
                    {
                        Modulus = queue.Dequeue(),
                        Exponent = queue.Dequeue(),
                        D = queue.Dequeue(),
                        P = queue.Dequeue(),
                        Q = queue.Dequeue(),
                        DP = queue.Dequeue(),
                        DQ = queue.Dequeue(),
                        InverseQ = queue.Dequeue()
                    });
                }
            }
        }

        #endregion

        #region LoadX509PublicKey

        public static void LoadX509PublicKey(this RSA rsa, string key)
        {
            LoadX509PublicKey(rsa, ConvertKey(key));
        }

        /// <summary>
        /// </summary>
        /// <param name="rsa"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static void LoadX509PublicKey(this RSA rsa, byte[] key)
        {
            using (var memoryStream = new MemoryStream(key))
            {
                using (var binaryReader = new BinaryReader(memoryStream))
                {
                    ushort num;
                    if ((num = binaryReader.ReadUInt16()) == 33072)
                    {
                        binaryReader.ReadByte();
                    }
                    else
                    {
                        if (num != 33328) throw new NotSupportedException();
                        binaryReader.ReadInt16();
                    }

                    if (!binaryReader.ReadBytes(15).SequenceEqual(new byte[]
                    {
                        48,
                        13,
                        6,
                        9,
                        42,
                        134,
                        72,
                        134,
                        247,
                        13,
                        1,
                        1,
                        1,
                        5,
                        0
                    }))
                        throw new NotSupportedException();
                    if ((num = binaryReader.ReadUInt16()) == 33027)
                    {
                        binaryReader.ReadByte();
                    }
                    else
                    {
                        if (num != 33283) throw new NotSupportedException();
                        binaryReader.ReadInt16();
                    }

                    if (binaryReader.ReadByte() != 0) throw new NotSupportedException();
                    if ((num = binaryReader.ReadUInt16()) == 33072)
                    {
                        binaryReader.ReadByte();
                    }
                    else
                    {
                        if (num != 33328) throw new NotSupportedException();
                        binaryReader.ReadInt16();
                    }

                    int num2;
                    if ((num = binaryReader.ReadUInt16()) == 33026)
                    {
                        num2 = binaryReader.ReadByte();
                    }
                    else
                    {
                        if (num != 33282) throw new NotSupportedException();
                        num2 = (binaryReader.ReadByte() << 8) | binaryReader.ReadByte();
                    }

                    if (binaryReader.ReadByte() == 0)
                        num2--;
                    else
                        binaryReader.BaseStream.Seek(-1L, SeekOrigin.Current);
                    var modulus = binaryReader.ReadBytes(num2);
                    if (binaryReader.ReadByte() != 2) throw new NotSupportedException();
                    rsa.ImportParameters(new RSAParameters
                    {
                        Modulus = modulus,
                        Exponent = binaryReader.ReadBytes(binaryReader.ReadByte())
                    });
                }
            }
        }

        #endregion
    }
}